/*************************************************************************
    > File Name: pstree.c
    > Author:		
    > Mail:		
    > Created Time: 2021年03月23日 星期二 16时25分47秒
 ************************************************************************/

#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define LINE_BUF 255
#define BUF_SIZE (1024*2)

struct name_uid {
	char pid[BUF_SIZE];
	char name[LINE_BUF];
	char uid[LINE_BUF];
};

int get_val(char *buf, const char *str, const char *key)
{

	int cnt = 0, off = 0;
	const char *p = str;
	while (*p != '\n' && *p != '\r') {
		cnt++;
		p++;
	}
	strncpy(buf, str, cnt);
	buf[cnt] = '\0';
	return 0;
}

int find_str_val(const char *buf, char *str_val, const char *key)
{
	int found = 0, key_len = strlen(key);
	const char *keyp = key ,*bufp = buf;
	char cmp[key_len];
	while (*keyp  && *bufp) {
		while (*keyp != *bufp++);
		strncpy(cmp, --bufp, key_len);
		cmp[key_len] = '\0';

		if (!strncmp(keyp, cmp, key_len)) {
			found = 1;
			/* buf from the proc/PIC/status, tailing with '\n' for one line */
			get_val(str_val, &bufp[key_len], key);
			return found;
		}
		bufp++;
	}

	return found;
}

int parse_file(char *file, struct name_uid *n_uid)
{

	int fd, val_len;
	char buf[BUF_SIZE];
	fd = open(file, O_RDONLY);
	if (fd < 0) {
		goto err0;
	}

	if(read(fd, buf, BUF_SIZE) < 0) {
		goto err1;
	}

	/* get string from Pid filed */
	if (!find_str_val(buf, n_uid->pid, "Pid:")) {
		goto err1;
	}

	/* get string from Name filed */
	if (!find_str_val(buf, n_uid->name, "Name:")) {
		goto err1;
	}

	/* get string from Uid filed */
	if (!find_str_val(buf, n_uid->uid, "Uid:")) {
		goto err1;
	}

	close(fd);
	return 0;
err1:
	close(fd);
err0:
	/* return -1 to try parse next /proc/PID/status */
	perror(strerror(errno));
	return -1;
}

int main(int argc, char *argv[]) {
        char *path = "/proc/", file_name[LINE_BUF];
	struct dirent *dent;
	struct name_uid n_uid;
	DIR *proc_dir;
	int d_num, stat;

	proc_dir = opendir(path);
	printf("%-10s%-40s%-20s\n", "PID", "Name", "UID");

	/* walk all directorys */
	while ((dent = readdir(proc_dir)) != NULL) {
		d_num = atoi(dent->d_name);
		/* skip the non-PID directory which not strart with number and not directroy */
		if ((dent->d_type == DT_DIR) && d_num) {
			sprintf(file_name, "%s%d%s", path, d_num, "/status");
			stat = parse_file(file_name, &n_uid);
			if (stat) {
				continue;
			}
			printf("%-10s%-40s%-20s\n", n_uid.pid, n_uid.name, n_uid.uid);
		}
	}

	closedir(proc_dir);
	return 0;
}
